Skip to content

feat(data): point-in-time fundamental fields (growth / valuation / quality) - #27

Open
agenthaulk wants to merge 4 commits into
fix/honest-unmappable-warningfrom
feature/fundamental-data-fields
Open

agenthaulk wants to merge 4 commits into
fix/honest-unmappable-warningfrom
feature/fundamental-data-fields

Conversation

@agenthaulk

Copy link
Copy Markdown
Owner

Summary

Wires the mounted lakehouse's fundamental source layer through to the workbench so a fundamentals idea — e.g. "年报、季报利润同比增加高的公司,未来21天更好" — parses to a real rank(netprofit_yoy) factor instead of the nearest price-volume approximation it produced before.

The raw data was already on the drive (source_snapshot/.../{income,financial,daily_basic,...} with ann_date announcement dates); only the pipeline was missing. This adds it as a layered, point-in-time-correct data plane.

What's added

  • data/fundamentals.py (public, path-free) — reads Tushare-Pro-schema statement parquet and expands each report to a daily point-in-time value via an ann_date as-of join: a figure is visible only from its announcement date, never the report period end, and never a report announced in the future (no look-ahead). Daily valuation ratios (daily_basic) merge on trade_date. 24 exposed fields across growth / profitability / valuation / leverage / cashflow (netprofit_yoy, or_yoy, roe, netprofit_margin, pe_ttm, pb, debt_to_assets, …).
  • data/local.py — the field catalog gains the fundamental fields from a single spec table (→ validation → MCP/LLM advertised surface, one edit propagates everywhere); the panel loader left-merges a PIT overlay (fundamentals.parquet sibling or a configured root); validate() is overlay-aware so the data-catalog availability is honest (FP-4, never a silent 0).
  • config — optional fundamentals_source_root / fundamentals_overlay_root (both None → price-volume-only, unchanged). Real paths live only in an ignored local config; no private path or provider name enters tracked code.
  • CLI qf data build-fundamentals — materializes the overlay from the source layer.
  • LLM prompt gains fundamental formula hints; the out-of-scope honesty warning narrows to genuinely-unavailable per-stock alt-data (sentiment / analyst / northbound), since fundamentals are now expressible.
  • Demo ships a synthetic overlay + a rank(netprofit_yoy) demo factor, so the field route works without the mounted drive.

OSS / policy

The processing functions are public; the source layout (a Tushare-Pro open schema) is a documented default; the mounted path lives only in a git-ignored local config. No network provider or ingestion client is bundled (consistent with the existing local source-snapshot loader precedent). release_safety_scan.py passes on 280 public files with zero private paths.

Tests / status

  • tests/test_fundamentals_pit.py pins the look-ahead correctness: value uses ann_date not period end; never pulls a future report; a restatement supersedes only from its own announcement; same-day dedup keeps the latest period.
  • Overlay-merge, catalog-expansion, and the existing surface tests (updated for the added fields/factor) all pass. Full suite 2001 passed; the demo rank(netprofit_yoy) factor evaluates end-to-end.

Relationship to the open stack

Stacked on #26 (honesty-warning) — this PR's §nl_flow narrows #26's out-of-scope terms now that fundamentals are supported. #26 in turn sits on #25 (integration docs). Recommended merge order: #25 → #26 → this. Real-mounted-drive acceptance (qf data build-fundamentals over the live source layer + browser reproduction) and an adversarial PIT review are being run as the final acceptance step.

Quant Forge contributors and others added 4 commits July 17, 2026 04:35
Wire the mounted lakehouse's fundamental source layer through to the
workbench so an idea like 年报季报利润同比 parses to a real
rank(netprofit_yoy) factor instead of a nearest price-volume approximation.

- data/fundamentals.py (public, path-free): reads Tushare-Pro-schema
  financial-statement parquet and expands each report to a daily
  point-in-time value via an ann_date as-of join -- a figure is visible only
  from its announcement date, never the period end, and never a report
  announced in the future (no look-ahead). Daily valuation ratios
  (daily_basic) merge on trade_date. 24 exposed fields across growth /
  profitability / valuation / leverage / cashflow.
- data/local.py: the field catalog gains the fundamental fields (single
  spec table -> catalog -> validation -> MCP/LLM surface); the panel loader
  left-merges a PIT overlay (fundamentals.parquet sibling or configured
  root); validate() is overlay-aware so availability is honest (FP-4).
- config: optional fundamentals_source_root / fundamentals_overlay_root
  (both None -> price-volume-only, unchanged). Real paths live only in an
  ignored local config; no private path or provider name enters tracked code.
- CLI qf data build-fundamentals materializes the overlay from the source
  layer. LLM prompt gains fundamental formula hints; the out-of-scope
  honesty warning narrows to genuinely-unavailable alt-data (sentiment /
  analyst / northbound), since fundamentals are now expressible. Demo
  workspace ships a synthetic overlay + a rank(netprofit_yoy) demo factor.
- tests: PIT look-ahead correctness (ann_date semantics, no future report,
  restatement supersedes only from its announcement), overlay merge, catalog
  expansion; existing surface tests updated for the added fields/factor.

release_safety_scan passes; full suite 2001 passed.
_runtime_paths_from_config rebuilt PathSettings but dropped
fundamentals_source_root/fundamentals_overlay_root, so
'qf data build-fundamentals' reported the source root unconfigured even when
the config set it. Carry both fields through; add an end-to-end CLI
regression that materializes an overlay from a synthetic source via --config.

Found by real-mounted-drive acceptance (materialized the live A-share
fundamentals overlay in 7.9s over 3.19M rows; PIT spot-check confirmed
value_at(D) = latest report announced on-or-before D, future announcements
excluded).
Opus review confirmed the PIT look-ahead property holds; fixes for the rest:

- portability: force all merge_asof join keys to datetime64[ns] (pandas>=2.2
  rejects a us/ns mismatch that pandas 3.x tolerated) -- caught by the
  container build.
- robustness: _parse_ymd coerces through nullable Int64 so a NaN-bearing int
  date column (which pandas upcasts to float -> '20250420.0') still parses
  instead of silently NaT-ing every row; as_of_expand guards the
  empty-reports case instead of crashing merge_asof on a dtype mismatch.
- remove the dead fundamentals_overlay_root config: it was never wired to any
  panel reader (only the CLI writer), so a configured overlay was invisible.
  Sibling fundamentals.parquet is the single discovery path; CLI --output
  overrides.
- loader de-dups overlay (trade_date,instrument) keys so a malformed overlay
  can't fan out the panel (its FP-4 contract).
- nl_flow keeps out-of-scope terms for balance-sheet line items with no
  exposed catalog field (goodwill/inventory/receivable/商誉/存货/应收).
- vendor-neutral wording (documented open schema, not a vendor name).
- document the restatement modeling choice (latest announcement, no look-ahead).

Tests added for the float/NaN date parse, empty-reports guard, and the
overlay duplicate-key fan-out.
Separate canonical field support from fields actually backed by the configured data root. Validate optional fundamental overlays before advertising or merging them, and reject dependent formulas before execution when data is absent, malformed, duplicated, unmatched, or entirely null. Project source parquet reads from schema metadata so files are not loaded fully twice.

Constraint: Price-volume research must remain usable when optional fundamentals are absent or invalid

Rejected: Silently deduplicate or ignore malformed fundamental data | hides data-quality failures from dependent formulas

Confidence: high

Scope-risk: moderate

Directive: Keep capability catalogs distinct from runtime field availability

Tested: 2017 pytest tests; release safety scan; compileall; diff check; independent code review

Not-tested: Real mounted-drive fundamental overlay rebuild
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant